package elephant.servers.webserver.core;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author icecooly
 *
 */
public class Dispatcher {
	private static Logger logger=LoggerFactory.getLogger(Dispatcher.class);
	//
	private Map<String,Controller>controllerMap;
	private Controller indexController;
	public Dispatcher() {
		controllerMap=new ConcurrentHashMap<String, Controller>();
	}
	/**
	 * 
	 * @param obj
	 */
	public void registerController(Object obj){
		Class<?>controllerClass=obj.getClass();
		RequestMapping cc=controllerClass.getAnnotation(RequestMapping.class);
		if(cc==null||cc.id()==null){
			throw new IllegalArgumentException("can not find RequestMapping");
		}
		if(controllerMap.containsKey(cc.id())){
			throw new IllegalArgumentException("controller :"+cc.id()+" already exists");
		}
		//
		logger.info("register controller:{}-{}",cc.id(),obj.getClass());
		Controller cs=new Controller(cc.id());
		cs.setInstance(obj);
		if(cc.index()){
			indexController=cs;
		}
		controllerMap.put(cc.id(),cs);
	}
	//
	public Context invokeService(Request request,Response response){
		Context ctx=new Context();
		ctx.request=request;
		ctx.response=response;
		Controller controller=null;
		List<String>querys=request.querys();
		if(querys.size()==0){
			controller=indexController;
		}
		//
		if(querys.size()>=1){
			controller=controllerMap.get(querys.get(0));
		}
		//
		if(controller==null){//404
			logger.warn("can not find controller:{}",request.queryURI());
			return ctx;
		}
		//invoke before web service if have
		if(!invokeBeforeMethod(controller, ctx)){
			return ctx;
		}
		WebService methodStub=null;
		//
		if(querys.size()<=1){
			methodStub=controller.indexMethod;
		}
		if(querys.size()>=2){
			methodStub=controller.getMethod(querys.get(1));
		}
		//
		if(methodStub==null){
			logger.warn("can not find service:{}",request.queryURI());
			return ctx;		
		}
		//
		Throwable t=null;
		try {
			methodStub.invokeMethod.invoke(controller.instance,ctx);
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
		//invoke after web service if have
		invokeAfterMethod(controller,ctx,t);
		return ctx;
	}
	//--------------------------------------------------------------------------
	public List<Controller> controllerStubs(){
		return new ArrayList<Controller>(controllerMap.values());
	}
	//
	private static boolean invokeBeforeMethod(Controller controller,Context ctx){
		if(controller.beforeInvokeMethod!=null){
			Throwable exception=null;
			boolean result=false;
			try {
				result=(boolean) controller.beforeInvokeMethod.invoke(controller.instance,ctx);
			} catch (IllegalAccessException e) {
				exception=e;
			} catch (IllegalArgumentException e) {
				exception=e;
			} catch (InvocationTargetException e){
				exception=e.getTargetException();
			}finally{
				if(logger.isDebugEnabled()){
					logger.debug("call before method:{}/{} ret:{}",
							controller.id,
							controller.beforeInvokeMethod.getName(),
							result);
				}
			}
			ctx.exception=exception;
			return result;
		}
		return true;
	}
	//
	private  void invokeAfterMethod(Controller controller,Context ctx,Throwable e){
		if(controller.afterInvokeMethod!=null){
			if(logger.isDebugEnabled()){
				logger.debug("call after method:{}#{}",
						controller.id,
						controller.afterInvokeMethod.getName());
			}
			try {
				controller.afterInvokeMethod.invoke(controller.instance,ctx,e);
			} catch (InvocationTargetException ee) {
				logger.error(ee.getMessage(),ee);
			} catch (Exception e1) {
				logger.error(e1.getMessage(),e1);
			} 
		}
	}
}
